package library.utils;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.KeyguardManager;
import android.app.Service;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.widget.Toast;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import library.logger.Logger;


public class Utils {

    private Utils() {
    }

    public static boolean hasImei(Context context) {
        try {
            TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            String imei = tm.getDeviceId();
            if (imei != null && !imei.equals("000000000000000")) {
                return true;
            }
        } catch (Exception ioe) {

        }
        return false;
    }

    private static void debug(Object obj) {
        Logger.debug(obj);
    }

    public static String getHandSetInfo(Context context) {
        if (context == null)
            return null;
        StringBuilder info = new StringBuilder();
        DisplayMetrics metrics = DisplayUtil.getMetrics();
        info.append(Utils.getBuildInfo(context) + "\n");
        info.append("Max Memory : " + (Runtime.getRuntime().maxMemory() / 1024 / 1024) + " M\n");
        info.append("PackageName: " + context.getPackageName() + "\n");
        info.append("Display    : " + metrics.widthPixels
                + " x "
                + metrics.heightPixels
                + " ( "
                + metrics.densityDpi
                + "  "
                + String.format("%.1f", metrics.density)
                + " ) "
        );
        return info.toString();
    }


    private static String getBuildInfo(Context context) {
        String handSetInfo = "DEV MODEL  : " + Build.MODEL +
                "\nAPI LEVEL  : " + Build.VERSION.SDK_INT +
                "\nOS VERSION : " + Build.VERSION.RELEASE +
                "\nDEBUG MODE : " + isDebuggable(context) +
                "\nAPK VERSION: " + ApkUtils.getVersionName(context) + "-" + ApkUtils.getVersionCode(context);
        TimeZone tz = TimeZone.getDefault();
        handSetInfo += "\nTIME ZONE  : " + tz.getID() + " ( " + tz.getDisplayName(false, TimeZone.SHORT) + " ) ";
        return handSetInfo;
    }

    public static boolean isDebuggable(Context context) {
        try {
            ApplicationInfo info = context.getApplicationInfo();
            return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
        } catch (Exception e) {
        }
        return false;
    }

    public static void toast(Context context, String msg) {
        if (context == null || TextUtils.isEmpty(msg))
            return;
        Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
    }

    private static long lastTriggerTime = 0;

    public static boolean isFastTrigger() {
        long now = System.currentTimeMillis();
        try {
            long interval = Math.abs(now - lastTriggerTime);
            if (interval < 150) {
                debug("Trigger Too Fast! " + now + "-" + lastTriggerTime + "=" + interval);
                return true;
            }
        } finally {
            lastTriggerTime = now;
        }
        return false;
    }

    public static boolean isTopActivity(Context context) {
        if (context != null)
            try {
                ActivityManager am = (ActivityManager) context.getSystemService(Activity.ACTIVITY_SERVICE);
                ComponentName cn = am.getRunningTasks(1).get(0).topActivity;
                if (cn.getClassName().contains(context.getClass().getName())) {
                    return true;
                }
            } catch (Exception e) {

            }
        return false;
    }

    public static void startAPP(Context context) {
        if (context == null) return;
        startAPP(context, context.getPackageName());
    }

    public static void startAPP(Context context, String packageName) {
        if (context == null) return;
        try {
            if (moveApp2Front(context, packageName))
                return;
            Intent intent = getLaunchIntent(context, packageName);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivitySafely(context, intent);
        } catch (Exception e) {
            debug(e);
        }
    }

    private static Intent getLaunchIntent(Context context, String packageName) {
        try {
            PackageManager pManager = context.getPackageManager();
            PackageInfo pi = pManager.getPackageInfo(packageName, 0);
            Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null);
            resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER);
            resolveIntent.setPackage(pi.packageName);
            List<ResolveInfo> apps = pManager.queryIntentActivities(resolveIntent, 0);

            Iterator<ResolveInfo> iterator = apps.iterator();
            while (iterator.hasNext()) {
                ResolveInfo ri = iterator.next();
                if (!ri.activityInfo.packageName.equals(packageName))
                    continue;
                packageName = ri.activityInfo.packageName;
                String className = ri.activityInfo.name;

                Intent intent = new Intent(Intent.ACTION_MAIN);
                intent.addCategory(Intent.CATEGORY_LAUNCHER);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent.setComponent(new ComponentName(packageName, className));
                return intent;
            }
        } catch (Exception e) {
            debug(e);
        }
        return null;
    }

    public static boolean moveApp2Front(Context context) {
        return moveApp2Front(context, context.getPackageName());
    }

    public static boolean moveApp2Front(Context context, String packageName) {
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
                ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
                List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(Integer.MAX_VALUE);
                if (tasks != null && !tasks.isEmpty()) {
                    for (ActivityManager.RunningTaskInfo task : tasks) {
                        ComponentName topActivity = task.topActivity;
                        if (topActivity.getPackageName().equals(packageName)) {
                            am.moveTaskToFront(task.id, Intent.FLAG_ACTIVITY_NEW_TASK);
                            return true;
                        }
                    }
                }
            }
        } catch (Exception e) {
            debug(e);
        }
        return false;
    }

    public static void moveApp2Back(Context context) {
        if (context instanceof Activity) {
            if (((Activity) context).moveTaskToBack(true)) {
                return;
            }
        }
    }

    public static void backToHome(Context context) {
        if (context == null) return;
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.addCategory(Intent.CATEGORY_HOME);
        if (startActivitySafely(context, intent)) {
            return;
        }

        PackageManager pm = context.getPackageManager();
        ResolveInfo homeInfo = pm.resolveActivity(intent, 0);
        ActivityInfo ai = homeInfo.activityInfo;
        intent.setComponent(new ComponentName(ai.packageName, ai.name));
        startActivitySafely(context, intent);
    }

    public static boolean startActivity(Context context, Class<? extends Activity> clz, int... flags) {
        if (context == null) return false;
        Intent intent = new Intent(context.getApplicationContext(), clz);
        if (!(context instanceof Activity)) {
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
        if (flags != null) {
            for (int flag : flags) {
                intent.addFlags(flag);
            }
        }
        return startActivitySafely(context, intent);
    }

    public static boolean startActivitySafely(Context context, Intent intent) {
        if (context != null && intent != null)
            try {
                context.startActivity(intent);
                return true;
            } catch (ActivityNotFoundException e) {
                debug(e);
            } catch (SecurityException e) {
                debug(e);
            } catch (Exception e) {
                debug(e);
            }
        return false;
    }

    public static void wakeup(Context context) {
        if (context == null) return;
        try {
            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
            KeyguardManager mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
            PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakeup");
            wakeLock.acquire();
            wakeLock.release();
            KeyguardManager.KeyguardLock mKeyguardLock = mKeyguardManager.newKeyguardLock("unLock");
            mKeyguardLock.disableKeyguard();
        } catch (Exception e) {

        }
    }


    public static void runMethods(Object obj, String regex, Object... args) {
        Pattern pattern = Pattern.compile(regex);
        Method[] methods = obj.getClass().getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            Matcher matcher = pattern.matcher(methods[i].getName());
            if (matcher.find()) {
                methods[i].setAccessible(true);
                try {
                    Object object = methods[i].invoke(obj, args);
                } catch (IllegalAccessException e) {
                    debug(e);
                } catch (InvocationTargetException e) {
                    debug(e);
                }
            }
        }
    }

    public static List<Object> getStringFieldsValue(Class clz, final Type fieldType) {
        List<Object> list = new ArrayList<>();
        if (clz == null)
            return list;
        Field[] fields = clz.getDeclaredFields();
        for (int j = 0; j < fields.length; j++) {
            try {
                Field field = fields[j];
                field.setAccessible(true);

                String name = field.getName();    //获取属性的名字
                Type type = field.getGenericType();    //获取属性的类型

//                simple(fieldType + " :  " + type + " " + name + " = " + field.get(null));
                if (type == fieldType) {   //如果type是类类型，则前面包含"class "，后面跟类名
                    Object value = field.get(null);
                    if (value != null)
                        list.add(value);
                }
            } catch (Exception e) {
                debug(e);
            }
        }
        return list;
    }

    public static String getMetaDataAtApplication(Context context, String key) {
        try {
            ApplicationInfo appInfo = context.getPackageManager()
                    .getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
            return appInfo.metaData.getString(key);
        } catch (Exception e) {
            debug(e);
        }
        return null;
    }

    public static String getMetaDataAtActivity(Activity context, String key) {
        try {
            ActivityInfo appInfo = context.getPackageManager()
                    .getActivityInfo(context.getComponentName(), PackageManager.GET_META_DATA);
            return appInfo.metaData.getString(key);
        } catch (Exception e) {
            debug(e);
        }
        return null;
    }

    public static String getMetaDataAtService(Context context, @NonNull Class<? extends Service> cls, String key) {
        try {
            ComponentName cn = new ComponentName(context, cls);
            ServiceInfo info = context.getPackageManager()
                    .getServiceInfo(cn, PackageManager.GET_META_DATA);
            return info.metaData.getString(key);
        } catch (PackageManager.NameNotFoundException e) {
            debug(e);
        }
        return null;
    }

    public static String getMetaDataAtReceviver(Context context, @NonNull Class<? extends Service> cls, String key) {
        try {
            ComponentName cn = new ComponentName(context, cls);
            ActivityInfo appInfo = context.getPackageManager()
                    .getReceiverInfo(cn, PackageManager.GET_META_DATA);
            return appInfo.metaData.getString(key);
        } catch (Exception e) {
            debug(e);
        }
        return null;
    }

    public static Map<String, Object> getFieldsValueMap(Class clz, final Type fieldType) {
        Map<String, Object> map = new HashMap<>();
        if (clz == null)
            return map;
        Field[] fields = clz.getDeclaredFields();
        for (int j = 0; j < fields.length; j++) {
            try {
                Field field = fields[j];
                field.setAccessible(true);

                String name = field.getName();    //获取属性的名字
                Type type = field.getGenericType();    //获取属性的类型

                if (type == fieldType) {   //如果type是类类型，则前面包含"class "，后面跟类名
                    Object value = field.get(null);
                    map.put(String.valueOf(name), value);
                }
            } catch (Exception e) {
                debug(e);
            }
        }
        return map;
    }

    /**
     * @param context
     * @return null, WIFI, 2G-CDMA,4G-LTE
     */
    public static String getNetworkType(Context context) {
        String networkType = null;
        String subTypeName = null;

        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();
        if (networkInfo != null && networkInfo.isConnected()) {
            if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
                networkType = "WIFI";
            } else if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
                subTypeName = networkInfo.getSubtypeName();
                // TD-SCDMA   subtype is 17
                int subtype = networkInfo.getSubtype();
                switch (subtype) {
                    case TelephonyManager.NETWORK_TYPE_GPRS:
                    case TelephonyManager.NETWORK_TYPE_EDGE:
                    case TelephonyManager.NETWORK_TYPE_CDMA:
                    case TelephonyManager.NETWORK_TYPE_1xRTT:
                    case TelephonyManager.NETWORK_TYPE_IDEN: //api<8 : replace by 11
                        networkType = "2G";
                        break;
                    case TelephonyManager.NETWORK_TYPE_UMTS:
                    case TelephonyManager.NETWORK_TYPE_EVDO_0:
                    case TelephonyManager.NETWORK_TYPE_EVDO_A:
                    case TelephonyManager.NETWORK_TYPE_HSDPA:
                    case TelephonyManager.NETWORK_TYPE_HSUPA:
                    case TelephonyManager.NETWORK_TYPE_HSPA:
                    case TelephonyManager.NETWORK_TYPE_EVDO_B: //api<9 : replace by 14
                    case TelephonyManager.NETWORK_TYPE_EHRPD:  //api<11 : replace by 12
                    case TelephonyManager.NETWORK_TYPE_HSPAP:  //api<13 : replace by 15
                        networkType = "3G";
                        break;
                    case TelephonyManager.NETWORK_TYPE_LTE:    //api<11 : replace by 13
                        networkType = "4G";
                        break;
                    default:
                        // http://baike.baidu.com/item/TD-SCDMA 中国移动 联通 电信 三种3G制式
                        if (subTypeName.equalsIgnoreCase("TD-SCDMA") || subTypeName.equalsIgnoreCase("WCDMA") || subTypeName.equalsIgnoreCase("CDMA2000")) {
                            networkType = "3G";
                        } else {
                            networkType = "unknown";
                        }
                        break;
                }
            }
        } else {
            return null;
        }
        if (TextUtils.isEmpty(subTypeName))
            return networkType;
        else
            return networkType + "-" + subTypeName;
    }

    public static boolean isNetworkAvailable(Context context) {
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (manager != null) {
            NetworkInfo info = manager.getActiveNetworkInfo();
            if (info != null && info.isAvailable() && info.isConnected()) {
//                NetworkInfo[] infos = manager.getAllNetworkInfo();
//                if (infos != null) {
//                    for (int i = 0; i < infos.length; i++) {
//                        if (infos[i].getState() == NetworkInfo.State.CONNECTED) {
//                            return true;
//                        }
//                    }
//                }
                return true;
            }
        }
        return false;
    }

    public static boolean isActivityRunning(final Context context) {
        try {
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(Integer.MAX_VALUE);
            if (tasks != null && !tasks.isEmpty()) {
                for (ActivityManager.RunningTaskInfo taskInfo : tasks) {
                    ComponentName componentName = taskInfo.topActivity;
                    if (componentName.getPackageName().equals(context.getPackageName())) {
                        return true;
                    }
                }
            }
        } catch (Exception e) {
            debug(e);
        }
        return false;
    }

    public static boolean isActivityRunningInBackground(final Context context) {
        try {
            ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
            if (tasks != null && !tasks.isEmpty()) {
                ComponentName topActivity = tasks.get(0).topActivity;
                if (!topActivity.getPackageName().equals(context.getPackageName())) {
                    if (isActivityRunning(context)) {
                        return true;
                    }
                }
            }
        } catch (Exception e) {
            debug(e);
        }
        return false;
    }

    public static boolean isApplicationRunningInBackground(Context context) {
        try {
            ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
            for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
                if (appProcess.processName.equals(context.getPackageName())) {
                    if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND) {
                        return true;
                    }
                }
            }
        } catch (Exception e) {
            debug(e);
        }
        return isActivityRunningInBackground(context);
    }

    public static boolean isServiceRunning(Context context, Class<? extends Service> service) {
        return isServiceRunning(context, service != null ? service.getName() : null);
    }

    public static boolean isServiceRunning(Context context, String className) {
        if (context != null && className != null)
            try {
                ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
                List<ActivityManager.RunningServiceInfo> serviceList = activityManager.getRunningServices(Integer.MAX_VALUE);
                if (serviceList != null && serviceList.size() > 0) {
                    for (int i = 0; i < serviceList.size(); i++) {
                        if (serviceList.get(i).service.getClassName().equals(className)) {
                            return true;
                        }
                    }
                }
            } catch (Exception e) {

            }
        return false;
    }

    public static List<ResolveInfo> getAllLauncher(Context context) {
        Intent intent = new Intent(Intent.ACTION_MAIN, null);
        intent.addCategory(Intent.CATEGORY_LAUNCHER);
        return context.getPackageManager().queryIntentActivities(intent, 0);
    }

//    public static boolean ping(String ip, String port) {
//        String str = "/system/bin/ping -c 1 -i 1 -w 2 " + ip + " " + port;
//        return Shell.cmd(str);
//    }

    public static boolean ping(final String ip, final int port, final int timeout) {
        InetSocketAddress endpointSocketAddr = new InetSocketAddress(ip, port);
        return ping(endpointSocketAddr, timeout);
    }

    public static boolean ping(final InetSocketAddress inetSocketAddress, final int timeout) {
        Boolean result = asyncTask(timeout, new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                boolean isReachable = false;
                Socket socket = null;
                try {
                    socket = new Socket();
                    socket.setSoTimeout(timeout);
                    // 端口号设置为 0 表示在本地挑选一个可用端口进行连接
//                    SocketAddress localSocketAddr = new InetSocketAddress(InetAddress.getLocalHost(), 0);
//                    socket.bind(localSocketAddr);
                    socket.connect(inetSocketAddress);
                    isReachable = true;
                } catch (Exception e) {
                } finally {
                    if (socket != null) {
                        try {
                            socket.close();
                        } catch (IOException e) {
                        }
                    }
                }
                return isReachable;
            }
        });
        return Boolean.parseBoolean(String.valueOf(result));
    }

    public static <T> T asyncTask(long timeout, Callable<T> callable) {
        ExecutorService executor = Executors.newCachedThreadPool();
        Future<T> future = executor.submit(callable);
        try {
            return future.get(timeout, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            debug(e);
        } catch (ExecutionException e) {
            debug(e);
        } catch (TimeoutException e) {
            debug(e);
        } catch (Exception e) {
            debug(e);
        } finally {
            future.cancel(true);
        }
        return null;
    }

    public static boolean isRemoteProcess(Context context) {
        String processAppName = getProcessName(context);
        if (processAppName == null || !processAppName.equalsIgnoreCase(context.getPackageName())) {
            // 则此application::onCreate 是被service 调用的，直接返回
            return true;
        }
        return false;
    }

    private static String getProcessName(Context context) {
        int pid = android.os.Process.myPid();
        String processName = null;
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List l = am.getRunningAppProcesses();
        Iterator i = l.iterator();
        PackageManager pm = context.getPackageManager();
        while (i.hasNext()) {
            ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo) (i.next());
            try {
                if (info.pid == pid) {
                    processName = info.processName;
                    return processName;
                }
            } catch (Exception e) {
                // Log.d("Process", "Error>> :"+ e.toString());
            }
        }
        return processName;
    }


    public static boolean isScreenOn(Context context) {
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        boolean screen = pm.isScreenOn();
        return screen;
    }

    public static boolean isScreenLock(Context context) {
        KeyguardManager mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
        if (mKeyguardManager.inKeyguardRestrictedInputMode()) {
            return true;
        }
        return false;
    }
}
